package cn.phantom.juctest;

import java.util.*;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class TestGenericDao {
    public static void main(String[] args) {

        GenericDao dao = new GenericDaoCached();
        System.out.println("===========> 查询");
        String sql = "select * from t_emp where id = ?";
        int id = 1;
        Emp emp = dao.queryOne(Emp.class, sql, id);
        System.out.println(emp.toString());

        emp = dao.queryOne(Emp.class, sql, id);
        System.out.println(emp.toString());

        emp = dao.queryOne(Emp.class, sql, id);
        System.out.println(emp.toString());

        System.out.println("===========> 更新");
        dao.update("update t_emp set salary = ? where id = ?", 1999.00, id);
        emp = dao.queryOne(Emp.class, sql, id);
        System.out.println(emp.toString());
    }
}

class GenericDaoCached extends GenericDao {

    private GenericDao dao = new GenericDao();
    private Map<SqlPair, Object> map = new HashMap<>();
    private ReentrantReadWriteLock rw = new ReentrantReadWriteLock();

    @Override
    public <T> List<T> queryList(Class<T> beanClass, String sql, Object... args) {
        return super.queryList(beanClass, sql, args);
    }

    @Override
    public <T> T queryOne(Class<T> beanClass, String sql, Object... args) {
        SqlPair key = new SqlPair(sql, args);
        rw.readLock().lock();

        try {
            // 先从缓存中找，找到直接返回
            T value = (T) map.get(key);
            if (value != null) {
                return value;
            }
        } finally {
            rw.readLock().unlock();
        }

        rw.writeLock().lock(); // 读锁释放掉才能获得写锁
        try {
            // 多个线程
            T value = (T) map.get(key);
            if (value == null) {
                // 缓存中没有再查数据库
                value = dao.queryOne(beanClass, sql, args);
                map.put(key, value);
            }
            return value;
        } finally {
            rw.writeLock().unlock();
        }
    }

    @Override
    public int update(String sql, Object... args) {
        rw.writeLock().lock();
        try {

            int update = dao.update(sql, args);
            map.clear();
            return update;
        } finally {
            rw.writeLock().unlock();
        }
        // 清空缓存
    }

    class SqlPair {
        private String sql;
        private Object[] args;

        public SqlPair(String sql, Object[] args) {
            this.sql = sql;
            this.args = args;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            SqlPair sqlPair = (SqlPair) o;
            return Objects.equals(sql, sqlPair.sql) && Arrays.equals(args, sqlPair.args);
        }

        @Override
        public int hashCode() {
            int result = Objects.hash(sql);
            result = 31 * result + Arrays.hashCode(args);
            return result;
        }
    }
}
